
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define fp(x) fputs(x, fo)
char para_num_buf[8];
const char* para_num(int i)
{
    sprintf(para_num_buf, "%d", i);
    return para_num_buf;
}

void write_temp_para(FILE*& fo, int pc, bool has_rtn)
{
    if(has_rtn || pc > 0) fp("(Of ");
    for(int i = 0; i < pc; i++)
    {
        if(i > 0) fp(", ");
        fp("T");
        fp(para_num(i));
    }
    if(has_rtn)
    {
        if(pc > 0) fp(", ");
        fp("RT");
    }
    if(has_rtn || pc > 0) fp(")");
}

void write_head(FILE*& fo, int pc, const char* fn, const char* dn, bool has_rtn, bool para_byref)
{
    fp("    Public ");
    if(has_rtn) fp("Function");
    else fp("Sub");
    fp(" ");
    fp(fn);
    write_temp_para(fo, pc, has_rtn);
    fp(" _"
       "\n                    (ByVal d As ");
    fp(dn);
    write_temp_para(fo, pc, has_rtn);
    for(int i = 0; i < pc; i++)
    {
        fp(",\n                     ");
        if(para_byref) fp("ByRef");
        else fp("ByVal");
        fp(" i");
        fp(para_num(i));
        fp(" As T");
        fp(para_num(i));
    }
    if(has_rtn) fp(",\n                     ByVal false_value As RT");
    fp(")");
    if(has_rtn) fp(" As RT");
    fp("\n");
}

void write_body(FILE*& fo, int pc, bool has_rtn)
{
    fp("            If d Is Nothing Then\n                Return");
    if(has_rtn) fp(" false_value");
    fp("\n            End If"
       "\n            Try"
       "\n                ");
    if(has_rtn) fp("Return ");
    fp("d(");
    for(int i = 0; i < pc; i++)
    {
        if(i > 0) fp(", ");
        fp("i");
        fp(para_num(i));
    }
    fp(")");
    fp("\n            Catch ex As ThreadAbortException"
       "\n                raise_error(error_type.warning, \"thread abort\")"
       "\n                Return");
    if(has_rtn) fp(" false_value");
    fp("\n            Catch ex As Exception"
       "\n                log_unhandled_exception(ex)"
       "\n                Return");
    if(has_rtn) fp(" false_value");
    fp("\n            End Try");
    if(has_rtn)
    fp("\n        End Function\n\n");
    else
    fp("\n        End Sub\n\n");
}

void write(FILE*& fo, int pc, const char* fn, const char* dn, bool has_rtn, bool para_byref)
{
    write_head(fo, pc, fn, dn, has_rtn, para_byref);
    write_body(fo, pc, has_rtn);
}
#undef fp

void write(FILE*& fo, int pc)
{
    write(fo, pc, "do_", "_do", true, true);
    write(fo, pc, "void_", "void", false, true);
    // .net 3.5 has only Action / Func with 4 parameters
    if(pc > 0 && pc <= 4)
    {
        write(fo, pc, "do_", "Func", true, false);
        write(fo, pc, "void_", "Action", false, false);
    }
}

int main(int argc, char* argv[])
{
    if(argc < 2)
        printf("need a parameter for output file.\n");
    else
    {
        const int para_count = 5;
        FILE* fo = fopen(argv[1], "w");

        fputs("\n'this file is generated by osi/root/codegen/delegate/delegate.exe"
              "\n'so edit osi/root/codegen/delegate/delegate.cpp instead of this file\n"
              "\nImports osi.root.delegates"
              "\nImports osi.root.constants.error_handle"
              "\nImports System.Threading\n"
              "\nPublic Module _delegate\n", fo);

        for(int i = 0; i <= para_count; i++)
            write(fo, i);

        fputs("End Module\n", fo);

        fclose(fo);
    }

    return 0;
}

